\documentclass[a4paper]{article}
\usepackage{fontspec,xunicode,xltxtra} % 加载 xetex 的宏包 
\usepackage{graphicx,amsmath}
\usepackage{makeidx}
\usepackage{array}
\usepackage{listings,xcolor}
\setsansfont[Mapping=tex-text]{Optima LT} % 使用 XeTeX 的 text-mapping 方案，正确显示LaTeX 样式的双引号（`` ''）
%\setmonofont{Consolas}

\setlength{\voffset}{-1cm}
\setlength{\hoffset}{-1cm}
\addtolength{\textwidth}{2cm}
\addtolength{\textheight}{2cm}
\linespread{1.3}

\lstset{language=VHDL, numbers=left, numberstyle=\tiny, breaklines, basicstyle=\ttfamily}

% 以下是中文字体的设置
\usepackage[slantfont，boldfont，xCJKnumber，xCJKtextspaces]{xeCJK} % 加载 xeCJK，允许斜体、粗体和 CJK 数字以及 CJK 对空格的设置
\setCJKmainfont[BoldFont={Adobe Heiti Std}, ItalicFont={Adobe Kaiti Std}]{Adobe Song Std}
\setCJKsansfont{Adobe Fangsong Std} 
\setCJKmonofont{Adobe Song Std}
\author{余舟迅\\2009011343\and 蓝昶\\2009011352 \and 李骥扬\\2009011329}
\title{\textbf{计算机组成原理实验：算术逻辑运算单元}}
\begin{document}
\maketitle

\section{实验目的}
\begin{enumerate}
\item 熟悉硬件描述语言及开发环境
\item 掌握简单运算器的数据传送通路
\item 验证运算器的功能
\item 熟悉TEC-2008教学实验系统
\end{enumerate}

\section{实验原理}
本实验内容是根据算术逻辑运算单元的功能表，通过状态机状态的变化，达到改变控制信号的组合的目的，从而实现不同的算术与逻辑运算功能，并将结果与标志位显示出来。实验中的ALU可以实现基本的算术运算、逻辑运算、移位运算等，功能如下表所示：

\begin{center}
\begin{tabular}{|c|c|c|}
\hline 
操作码 & 功能 & 描述\\
\hline 
ADD(1000) & A+B+Cin & 加法\\
\hline
SUB(0111) & A-B-Cin & 减法\\
\hline
AND(0110) & A and B & 与\\
\hline
OR(0101) & A or B & 或\\
\hline
XOR(0100) & A xor B & 异或\\
\hline
NOT(0011) & not A & 非\\
\hline
SLL(0010) & A sll B & 逻辑左移\\
\hline
SRA(0001) & A sra B & 算术右移\\
\hline
ROL(0000) & A rol B & 循环左移\\
\hline
\end{tabular}
\end{center}

\section{实现原理}
ALU由VHDL实现，其中设置2个输入端口：时钟信号\verb|clk|和复位信号\verb|rst|；18个输出端口：16位输出\verb|y|、1位进位标志位\verb|c|和1位零标志位\verb|c|。

对于输入信号，操作数\verb|a|和\verb|b|在代码内指定为16位STD\_LOGIC\_VECTOR信号，输入进位标志位\verb|cin|指定为1。

为了测试ALU，我们使用一个9状态的状态机产生从\verb|0000|到\verb|1000|的操作码(opcode)作为ALU的控制输入。ALU根据控制码进行不同的运算操作并产生输出。下面简要介绍各个功能的实现方法。

\subsection{逻辑与移位操作}
由于VHDL语言本身提供了ROL、SRA和SLL三个移位运算符和AND、OR、XOR、NOT四个逻辑运算符，因此大大减低了实现难度。值得注意的是，移位运算符的左操作数应为BIT\_VECTOR类型、右操作数应为INTEGER类型，故要先把STD\_LOGIC\_VECTOR类型的数据A转换成BIT\_VECTOR类型、数据B转换成INTEGER类型再进行运算操作，移位后的结果仍然是BIT\_VECTOR类型，故转换回STD\_LOGIC\_VECTOR类型输出。

\subsection{算术运算操作}
VHDL语言中提供了对位向量的加法和减法功能，但是进位标志需要自己实现。我们采取的办法是将操作数扩展为17位，其中最高位设置为0，结果用一个17位的中间变量储存，低16位为结果输出，最高位作为进位输出。这样操作的好处是速度快效率高，节省代码复杂度。

\subsection{零标志位}
以上操作后的结果都保存在一个名为\verb|output|的\textsf{变量}中，最后如果变量\verb|output|的值为\verb|0000000000000000|，则标志位\verb|z|输出1，否则置0。

\section{VHDL代码}
\begin{lstlisting}
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity alu is
    Port ( clk : in  STD_LOGIC;
           rst : in  STD_LOGIC;
			  y : out STD_LOGIC_VECTOR(15 downto 0);
           c : out  STD_LOGIC :='1';
           z : out  STD_LOGIC);
end alu;

architecture Behavioral of alu is
	signal sig : STD_LOGIC_VECTOR(3 downto 0) := "0000";
	signal a : STD_LOGIC_VECTOR(15 downto 0) :="0000000000000001";
	signal b : STD_LOGIC_VECTOR(15 downto 0) :="0000000000000010";
	signal cin : STD_LOGIC := '0';
begin
	process(clk)
	begin
		if(clk'event and clk='1') then
			if(sig="1000" or rst='1') then
				sig <= "0000";
			else
				sig <= sig + 1;
			end if;
		end if;
	end process;
	
	process(sig)
	variable output : STD_LOGIC_VECTOR(15 downto 0);
	variable i_sum : STD_LOGIC_VECTOR(16 downto 0);
	constant i_cin_ext: STD_LOGIC_VECTOR(16 downto 1) := (others => '0');
	begin
		--rol
		if(sig="0000") then
			output := To_StdLogicVector(TO_BITVECTOR(a) rol CONV_INTEGER(b));
		end if;
		--sra
		if(sig="0001") then
			output := To_StdLogicVector(TO_BITVECTOR(a) sra CONV_INTEGER(b));
		end if;
		--sll
		if(sig="0010") then
			output := To_StdLogicVector(TO_BITVECTOR(a) sll CONV_INTEGER(b));
		end if;
		--not
		if(sig="0011") then
			output := not a;
		end if;
		--xor
		if(sig="0100") then
			output := a xor b;
		end if;
		--or
		if(sig="0101") then
			output := a or b;
		end if;
		--and
		if(sig="0110") then
			output := a and b;
		end if;
		--sub
		if(sig="0111") then
			i_sum := ('0' & a) - ('0' & b) - (i_cin_ext & cin); 
			output := i_sum(15 downto 0); 
			c <= i_sum(16); 
		end if;
		--add
		if(sig="1000") then
			i_sum := ('0' & a) + ('0' & b) + (i_cin_ext & cin); 
			output := i_sum(15 downto 0); 
			c <= i_sum(16); 
		end if;

		if(output="0000000000000000") then
			z <= '1';
		else 
			z <= '0';
		end if;
		y <= output;
	end process;
end Behavioral;
\end{lstlisting}

\section{仿真}
我们采用Xilinx ISE 10.1内置的仿真功能，从仿真波形图中可以看到，ALU各个功能工作正常。
\begin{figure}[!htb]
\centering
\includegraphics[width=0.8\textwidth]{01.jpg}
\caption{仿真波形}
\end{figure}

\section{调试与故障排除}
实验中，减法的结果与仿真结果不同。经过排查，发现是代码的一个变量名输入错误，真正要用的变量未被初始化，因此在实验箱中测试的结果就是不可预期的值。之所以在之前的仿真中没有发现这个错误，是因为仿真器中所有变量默认都被初始化为0。因此我们不应该盲目依赖仿真结果，仍然要做好代码的检查工作。

\section{实验收获}
\begin{enumerate}
\item 熟悉Xilinx ISE开发环境和VHDL语言
\item 熟悉TEC-2008实验箱
\item 掌握了基本的查错方法
\end{enumerate}
\end{document}
